module JpMailer::Base64

  NKF_OUTPUT_OPTIONS = {
    'iso-2022-jp' => 'j',
    'utf-8' => 'w',
    'shift_jis' => 's'
  }

  def self.get_nkf_option(str)
    NKF_OUTPUT_OPTIONS[str] ? NKF_OUTPUT_OPTIONS[str] : str
  end

  def self.get_nkf_charset(str)
    key = NKF_OUTPUT_OPTIONS.index(str)
    key ? key : str
  end

  def self.header_encode(charset, str)
    /(.*?)<(.*?)>/ =~ str
    return $& ? "#{ encode_with_header(charset, $1)} <#{$2}>" : str
  end

  def self.encode_with_header(charset, str, &block)
    return "" if str.nil? || str.empty?
    str = if block_given?
            encode(charset, str, &block)
          else
            encode(charset, str)
          end

    encoded_str = ""
    str.split(/\n/n).each_with_index do |part, index|
      encoded_str += "#{ "\n " if index > 0}=?#{get_nkf_charset(charset)}?B?#{part}?="
    end
    encoded_str
  end

  # +str+に対し、+charset+で指定した文字コードでBASE64エンコードを行う。
  # ブロックを渡すと、文字コード変換後、BASE64エンコードを行う前に実行される。
  # Base64エンコード時に1行が長くなりすぎないようにカットするが、
  # ISO-2022-JP等7ビット文字列は文字を壊さないようにカットするのが難しいため、
  # UTF8で十分短い文字列に分割して変換を行う。
  def self.encode(charset, str, &block)
    case charset
    when 'iso-2022-jp'
      # 文字コード判別に失敗したらUTF-8と仮定して処理を進める
      parts = case NKF.guess(str)
              when NKF::UNKNOWN, NKF::ASCII
                str.split(/(.{15})/u)
              else
                Kconv.kconv(str, Kconv::UTF8, Kconv.guess(str)).split(/(.{15})/u)
              end
      parts.reject{ |s| s.blank? }.collect { |s|
        s = NKF.nkf("-jxW", s)
        s = block.call(charset, s) if block_given?
        [s].pack("m").chomp.gsub(/\n/, '')
      }.join("\n")
    when  'utf-8'
      # 文字コード判別に失敗したらUTF-8と仮定して処理を進める
      str = case NKF.guess(str)
            when NKF::UNKNOWN, NKF::ASCII
              NKF.nkf("-Wwx", str)
            else
              Kconv.kconv(str, Kconv::UTF8, Kconv.guess(str))
            end
      str = block.call(charset, str) if block_given?
      str.split(/(.{15})/u).reject{ |s| s.blank? }.collect { |s|
        [s].pack("m").chomp.gsub(/\n/, '')
      }.join("\n")
    when 'shift_jis'
      # 文字コード判別に失敗したらUTF-8と仮定して処理を進める
      str = case NKF.guess(str)
            when NKF::UNKNOWN, NKF::ASCII
              NKF.nkf("-Wsx", str)
            else
              Kconv.kconv(str, Kconv::SJIS, Kconv.guess(str))
            end
      str = block.call(charset, str) if block_given?
      str.split(/(.{25})/s).reject{ |s| s.blank? }.collect { |s|
        [s].pack("m").chomp.gsub(/\n/, '')
      }.join("\n")
    end
  end

end
